package org.ms.mconf.sdk.service.support;

import lombok.extern.slf4j.Slf4j;
import org.micro.URL;
import org.ms.mconf.sdk.Constants;
import org.ms.mconf.sdk.exception.MicroConfigException;
import org.ms.mconf.sdk.model.MetaMultiDataConfig;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.error.YAMLException;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;

/**
 * The Abstract Fail back Service
 *
 * @author lry
 */
@Slf4j
public abstract class FailBackService extends AbstractService {

    private String cachePath;
    private CacheConfig cacheConfig;
    private final Yaml yml = new Yaml();
    private volatile int ymlCacheHashCode = 0;

    @Override
    public void initialize(URL url) throws MicroConfigException {
        super.initialize(url);
        this.cachePath = Constants.get(url, Constants.CACHE_FILE);
        log.info("The cache path is: {}", cachePath);

        try {
            this.cacheConfig = yml.load(new FileInputStream(cachePath));
            this.ymlCacheHashCode = cacheConfig.hashCode();
        } catch (FileNotFoundException e) {
            log.warn("The not found file: {}", cachePath);
        } catch (YAMLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public List<MetaMultiDataConfig> pull(String application, String... configs) throws MicroConfigException {
        List<MetaMultiDataConfig> metaMultiDataConfigs = new ArrayList<>();

        try {
            metaMultiDataConfigs = this.pullCall(application, configs);
            this.checkAndUpdateCache(metaMultiDataConfigs);

            return metaMultiDataConfigs;
        } catch (Throwable t) {
            log.error("The processor pull is Exception: application=[" + application + "], configs=[" + Arrays.toString(configs) + "]", t);
            if (cacheConfig == null || cacheConfig.getMicroConfigs() == null) {
                throw t;
            }

            Map<String, MetaMultiDataConfig> readCacheConfigs = cacheConfig.getMicroConfigs().get(application);
            if (readCacheConfigs == null || readCacheConfigs.isEmpty()) {
                throw t;
            }
            if (configs == null || configs.length == 0) {
                return new ArrayList<>(readCacheConfigs.values());
            }

            for (String config : configs) {
                if (!readCacheConfigs.containsKey(config)) {
                    throw t;
                }
                metaMultiDataConfigs.add(readCacheConfigs.get(config));
            }

            return metaMultiDataConfigs;
        }
    }

    @Override
    public void destroy() throws MicroConfigException {
        super.destroy();
    }

    private void checkAndUpdateCache(List<MetaMultiDataConfig> metaMultiDataConfigs) {
        try {
            CacheConfig tempCacheConfig = new CacheConfig(application, metaMultiDataConfigs);
            if (cacheConfig != null) {
                Map<String, MetaMultiDataConfig> memoryMap = cacheConfig.getMicroConfigs().computeIfAbsent(application, k -> new HashMap<>());
                Map<String, MetaMultiDataConfig> nowMap = tempCacheConfig.getMicroConfigs().get(application);
                if (nowMap != null && !nowMap.isEmpty()) {
                    memoryMap.putAll(nowMap);
                }
            } else {
                cacheConfig = tempCacheConfig;
            }

            if (ymlCacheHashCode != cacheConfig.hashCode()) {
                ymlCacheHashCode = cacheConfig.hashCode();
                if (!cacheConfig.getMicroConfigs().get(application).isEmpty()) {
                    try {
                        yml.dump(cacheConfig, new FileWriter(cachePath));
                        log.info("The dump cache config: {} -> {}", ymlCacheHashCode, cacheConfig);
                    } catch (IOException e) {
                        log.error("The dump yaml cache is Exception", e);
                    }
                }
            }
        } catch (Exception e) {
            log.error("The check and update cache is Exception", e);
        }
    }

    /**
     * The processor pull
     *
     * @param application application id
     * @param configs     config id list
     * @return {@link MetaMultiDataConfig}
     * @throws MicroConfigException Micro Config Exception {@link MicroConfigException}
     */
    protected abstract List<MetaMultiDataConfig> pullCall(String application, String... configs) throws MicroConfigException;

}
